/**
 * Copyright (c) 2015-2019, Golly Hu 胡高 (3722711@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package cn.hg.jboot.core.directive;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.jfinal.plugin.activerecord.SqlPara;
import com.jfinal.plugin.activerecord.sql.JbsaDirectiveBase;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;

import io.jboot.web.directive.annotation.JFinalDirective;

/**
 * @author Golly
 */
@JFinalDirective(LikeParaDirective.DIRECTIVE_NAME)
public class LikeParaDirective extends JbsaDirectiveBase {

    /**
     * 指令名称
     */
    public static final String DIRECTIVE_NAME = "likePara";

    @Override
    public void onRender(Env env, Scope scope, Writer writer) {
        SqlPara sqlPara = (SqlPara)scope.get(SQL_PARA_KEY);

        if (sqlPara == null) {
            throw new TemplateException("#" + DIRECTIVE_NAME + " directive invoked by getSqlPara(...) method only",
                this.location);
        }

        Object[] paraArray = this.exprList.evalExprList(scope);

        List<Object> paraList = new ArrayList<>();

        /*
         * 首先将参数全规划成List
         */
        for (Object para : paraArray) {
            if (para == null) { // 优先处理参数为Null的情况
                throw new ParseException("The parameter of #" + DIRECTIVE_NAME + " directive can not be null",
                    this.location);
            } else if (para instanceof Collection<?>) { // 处理参数为Collection的情况
                paraList.addAll((Collection<?>)para);
            } else if (para.getClass().isArray()) { // 处理参数为Array的情况
                for (Object object : (Object[])para) {
                    paraList.add(object);
                }
            } else { // 处理参数为一般类型的情况
                paraList.add(para);
            }
        }

        boolean leftLike = false;
        boolean rightLike = false;

        /*
         * 对规划好的参数List进行处理
         */
        // 处理左百分号（%）
        if ("%".equals(paraList.get(0))) {
            // 单独为百分号的情况
            leftLike = true;
            paraList.remove(0);
        } else if (String.valueOf(paraList.get(0)).startsWith("%")) {
            // 百分号开头的情况
            leftLike = true;
            paraList.set(0, String.valueOf(paraList.get(0)).substring(1));
        }

        // 处理右百分号（%）
        if (paraList.size() > 0) {
            int lastIndex = paraList.size() - 1;
            if ("%".equals(paraList.get(lastIndex))) {
                // 单独为百分号的情况
                rightLike = true;
                paraList.remove(lastIndex);
            } else if (String.valueOf(paraList.get(lastIndex)).endsWith("%")) {
                // 百分号结尾的情况
                rightLike = true;
                paraList.set(lastIndex,
                    String.valueOf(paraList.get(0)).substring(0, String.valueOf(paraList.get(0)).length() - 1));
            }
        }

        /*
         * 构建LIKE参数字串
         */
        StringBuilder sb = new StringBuilder();
        // 左百分号（%）
        sb.append(leftLike ? '%' : "");
        sb.append(!(leftLike || rightLike) ? '%' : ""); // 左右都不包括百分号，则默认加上
        // 参数字串
        for (Object para : paraList) {
            sb.append(para);
        }
        // 右百分号（%）
        sb.append(rightLike ? '%' : "");
        sb.append(!(leftLike || rightLike) ? '%' : ""); // 左右都不包括百分号，则默认加上

        /*
         * 将解析好的参数给设置到SQL模板中
         */
        this.write(writer, "LIKE ?");

        sqlPara.addPara(sb.toString());
    }

    @Override
    public void setExprList(ExprList exprList) {

        if (exprList.length() == 0) {
            throw new ParseException("The parameter of #" + DIRECTIVE_NAME + " directive can not be blank",
                this.location);
        }

        this.exprList = exprList;
    }

}
